package compilador.sintatico;

import compilador.lexico.AnalisadorLexico;
import compilador.lexico.LexicoException;
import compilador.lexico.TipoToken;
import compilador.lexico.Token;

/**
 * Faz o reconhecimento da gramática da linguagem, usando o analisador léxico
 * par aobter os tokens.
 *
 * @author Carlos H V Pinto
 * @author Lucas S Bueno
 * @author Paulo G L Freire
 */
public final class AnalisadorSintatico {

    private AnalisadorLexico lexico;
    private Token tokenAtual;

    /**
     * Cria e inicializa um analisador sintático.
     *
     * @param lexico O analisador léxico para fazer o reconhecimento dos
     * símbolos.
     */
    public AnalisadorSintatico(AnalisadorLexico lexico) {
        this.lexico = lexico;
        this.tokenAtual = null;
    }

    /**
     * Inicia o processo de análise sintática recursiva preditiva à esquerda.
     * Caso ocorram erros, imprime a mensagem de erro e finaliza a compilação.
     */
    public void iniciaAnalise() {
        // Chama a regra raiz, que deriva todas as outras        
        try {
            obterSimbolo();
            programa();
        } catch (SintaticoException erro) {
            System.err.println(erro.getMessage());
        } catch (LexicoException erro) {
            System.err.println(erro.getMessage());
        }
        System.out.println("Fim da compilacao");
    }

    /**
     * Monta uma mensagem de erro sintático. Após a montagem da mensagem uma
     * exceção de erro sintático é lançada com esta mensagem.
     *
     * @throws SintaticoException
     */
    public void erro() {
        String mensagem = "Linha " + lexico.getLeitor().getLinha()
                + ": erro sintatico proximo a ";
        // Imprime o erro sintático
        if (tokenAtual == null) {
            mensagem += "EOF";
        } else {
            mensagem += tokenAtual.getLexema();
        }

        throw new SintaticoException(mensagem);
    }

    /**
     * Chama o analisador léxico para ler o próximo token do arquivo de entrada,
     * e atualiza o token atual.
     */
    public void obterSimbolo() {
        // Lê até encontrar um token, imprimindo possíveis erros léxicos
        do {
            tokenAtual = lexico.proximoToken();
        } while (!lexico.getLeitor().terminou() && tokenAtual == null);

        if (tokenAtual == null) {
            erro();
        }
    }

    /**
     * Tenta reconhecer um tipo de token, lançando erro em caso de falha
     *
     * @param tipo Tipo de token esperado
     */
    public void reconheceToken(TipoToken tipo, boolean obrigatorio) {
        if (tipo == tokenAtual.getTipo()) {
            obterSimbolo();
        } else if (obrigatorio) {
            // Imprime o erro            
            erro();
        }
    }

    /**
     * Tenta reconhecer um tipo de token, lançando erro em caso de falha
     *
     * @param tipos Vetor com os tipos de tokens esperados. Pelo menos um deles
     * precisa ser reconhecido.
     */
    public void reconheceToken(TipoToken[] tipos, boolean obrigatorio) {
        // Tenta reconhecer um por um
        for (TipoToken tipo : tipos) {
            // Obtém o próximo símbolo e atualiza o token atual
            if (tipo == tokenAtual.getTipo() && !lexico.getLeitor().terminou()) {
                obterSimbolo();
                return;
            }
        }
        // Se chegou aqui, é porque não conseguiu reconhecer nenhum
        if (obrigatorio) {
            // Imprime o erro            
            erro();
        }
    }

    /**
     * programa : declaracoes 'algoritmo' (declaracao_local)* (cmd)*
     * 'fimalgoritmo';
     */
    public void programa() {
        declaracoes();
        reconheceToken(TipoToken.PR_Algoritmo, true);
        // (declaracao_local)*
        while (tokenAtual.getTipo() == TipoToken.PR_Declare
                || tokenAtual.getTipo() == TipoToken.PR_Constante
                || tokenAtual.getTipo() == TipoToken.PR_Tipo) {
            declaracaoLocal();
        }
        // (cmd)*
        while (tokenAtual.getTipo() == TipoToken.PR_Leia
                || tokenAtual.getTipo() == TipoToken.PR_Escreva
                || tokenAtual.getTipo() == TipoToken.PR_Se
                || tokenAtual.getTipo() == TipoToken.PR_Caso
                || tokenAtual.getTipo() == TipoToken.PR_Para
                || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                || tokenAtual.getTipo() == TipoToken.PR_Faca
                || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                || tokenAtual.getTipo() == TipoToken.Identificador
                || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
            cmd();
        }
        if (tokenAtual.getTipo() != TipoToken.PR_FimAlgoritmo) {
            erro();
        }
    }

    /**
     * declaracoes : (declaracao_local | declaracao_global) declaracoes | ;
     */
    public void declaracoes() {
        boolean epsilon = false;

        switch (tokenAtual.getTipo()) {
            case PR_Declare:
            case PR_Constante:
            case PR_Tipo:
                declaracaoLocal();
                break;
            case PR_Procedimento:
            case PR_Funcao:
                declaracaoGlobal();
                break;
            default:
                epsilon = true;
        }
        if (!epsilon) {
            declaracoes();
        }
    }

    /**
     * declaracao_local : 'declare' variavel | 'constante' IDENTIFICADOR ':'
     * ('literal' | 'inteiro' | 'real' | 'logico') '=' (CADEIA_LITERAL |
     * NUMERO_INTEIRO | NUMERO_REAL | 'verdadeiro' | 'falso') | 'tipo'
     * IDENTIFICADOR ':' tipo ;
     */
    public void declaracaoLocal() {
        switch (tokenAtual.getTipo()) {
            case PR_Declare:
                obterSimbolo();
                variavel();
                break;
            case PR_Constante:
                obterSimbolo();
                reconheceToken(TipoToken.Identificador, true);
                reconheceToken(TipoToken.SR_DoisPontos, true);
                TipoToken[] tipos = {TipoToken.PR_Literal, TipoToken.PR_Inteiro,
                    TipoToken.PR_Real, TipoToken.PR_Logico};
                reconheceToken(tipos, true);
                reconheceToken(TipoToken.SR_Igual, true);
                TipoToken[] tipos2 = {TipoToken.CadeiaLiteral, TipoToken.NumeroInteiro,
                    TipoToken.NumeroReal, TipoToken.PR_Verdadeiro, TipoToken.PR_Falso};
                reconheceToken(tipos2, true);
                break;
            case PR_Tipo:
                obterSimbolo();
                reconheceToken(TipoToken.Identificador, true);
                reconheceToken(TipoToken.SR_DoisPontos, true);
                tipo();
                break;
            default:
                erro();
        }
    }

    /**
     * declaracao_global : 'procedimento' IDENTIFICADOR '(' (parametro)? ')'
     * (declaracao_local)* (cmd)* 'fimprocedimento' | 'funcao' IDENTIFICADOR '('
     * (parametro)? ')' ':' ('^')? (('literal' | 'inteiro' | 'real' | 'logico')
     * | IDENTIFICADOR) (declaracao_local)* (cmd)* 'fimfuncao' ;
     */
    public void declaracaoGlobal() {
        switch (tokenAtual.getTipo()) {
            case PR_Procedimento:
                obterSimbolo();
                reconheceToken(TipoToken.Identificador, true);
                reconheceToken(TipoToken.SR_AbreParenteses, true);
                if (tokenAtual.getTipo() != TipoToken.SR_FechaParenteses) {
                    parametro();
                }
                reconheceToken(TipoToken.SR_FechaParenteses, true);
                // (declaracao_local)*
                while (tokenAtual.getTipo() == TipoToken.PR_Declare
                        || tokenAtual.getTipo() == TipoToken.PR_Constante
                        || tokenAtual.getTipo() == TipoToken.PR_Tipo) {
                    declaracaoLocal();
                }
                // (cmd)*
                while (tokenAtual.getTipo() == TipoToken.PR_Leia
                        || tokenAtual.getTipo() == TipoToken.PR_Escreva
                        || tokenAtual.getTipo() == TipoToken.PR_Se
                        || tokenAtual.getTipo() == TipoToken.PR_Caso
                        || tokenAtual.getTipo() == TipoToken.PR_Para
                        || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                        || tokenAtual.getTipo() == TipoToken.PR_Faca
                        || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                        || tokenAtual.getTipo() == TipoToken.Identificador
                        || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                    cmd();
                }
                reconheceToken(TipoToken.PR_FimProcedimento, true);
                break;
            case PR_Funcao:
                obterSimbolo();
                reconheceToken(TipoToken.Identificador, true);
                reconheceToken(TipoToken.SR_AbreParenteses, true);
                if (tokenAtual.getTipo() != TipoToken.SR_FechaParenteses) {
                    parametro();
                }
                reconheceToken(TipoToken.SR_FechaParenteses, true);
                reconheceToken(TipoToken.SR_DoisPontos, true);
                // ('^')?
                reconheceToken(TipoToken.SR_Circunflexo, false);
                // (('literal' | 'inteiro' | 'real' | 'logico') | IDENTIFICADOR)
                if (tokenAtual.getTipo() == TipoToken.Identificador) {
                    obterSimbolo();
                } else {
                    TipoToken[] tipos = {TipoToken.PR_Literal, TipoToken.PR_Inteiro,
                        TipoToken.PR_Real, TipoToken.PR_Logico};
                    reconheceToken(tipos, true);
                }
                // (declaracao_local)*
                while (tokenAtual.getTipo() == TipoToken.PR_Declare
                        || tokenAtual.getTipo() == TipoToken.PR_Constante
                        || tokenAtual.getTipo() == TipoToken.PR_Tipo) {
                    declaracaoLocal();
                }
                // (cmd)*
                while (tokenAtual.getTipo() == TipoToken.PR_Leia
                        || tokenAtual.getTipo() == TipoToken.PR_Escreva
                        || tokenAtual.getTipo() == TipoToken.PR_Se
                        || tokenAtual.getTipo() == TipoToken.PR_Caso
                        || tokenAtual.getTipo() == TipoToken.PR_Para
                        || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                        || tokenAtual.getTipo() == TipoToken.PR_Faca
                        || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                        || tokenAtual.getTipo() == TipoToken.Identificador
                        || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                    cmd();
                }
                reconheceToken(TipoToken.PR_FimFuncao, true);
                break;
            default:
                erro();
        }
    }

//    /**
//     * tipo_basico : ('literal' | 'inteiro' | 'real' | 'logico') ;
//     */
//    public void tipoBasico() {
//        TipoToken[] tipos = {TipoToken.PR_Literal, TipoToken.PR_Inteiro,
//            TipoToken.PR_Real, TipoToken.PR_Logico};
//        reconheceToken(tipos, true);
//    }
    /**
     * variavel : identificador (',' identificador)* ':' tipo ;
     */
    public void variavel() {
        identificador();
        // (',' identificador)*
        while (tokenAtual.getTipo() == TipoToken.SR_Virgula) {
            obterSimbolo();
            identificador();
        }
        reconheceToken(TipoToken.SR_DoisPontos, true);
        tipo();
    }

    /**
     * identificador : ('^')? IDENTIFICADOR ('.' IDENTIFICADOR)* dimensao+ ;
     */
    public void identificador() {
        // ('^')?
        reconheceToken(TipoToken.SR_Circunflexo, false);
        reconheceToken(TipoToken.Identificador, true);
        // ('.' IDENTIFICADOR)*
        while (tokenAtual.getTipo() == TipoToken.SR_Ponto) {
            obterSimbolo();
            reconheceToken(TipoToken.Identificador, true);
        }
        dimensao();

    }

    /**
     * dimensao : '[' exp_aritmetica ']' | ;
     */
    public void dimensao() {
        if (tokenAtual.getTipo() == TipoToken.SR_AbreColchetes) {
            obterSimbolo();
            expAritmetica();
            reconheceToken(TipoToken.SR_FechaColchetes, true);
            dimensao();
        }
    }

    /**
     * tipo : 'registro' (variavel)+ 'fimregistro' | ('^')? (('literal' |
     * 'inteiro' | 'real' | 'logico') | IDENTIFICADOR) ;
     */
    public void tipo() {
        switch (tokenAtual.getTipo()) {
            case PR_Registro:
                obterSimbolo();
                variavel();
                while (tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                        || tokenAtual.getTipo() == TipoToken.Identificador) {
                    variavel();
                }
                reconheceToken(TipoToken.PR_FimRegistro, true);
                break;
            case SR_Circunflexo:
            case Identificador:
            case PR_Literal:
            case PR_Inteiro:
            case PR_Real:
            case PR_Logico:
                reconheceToken(TipoToken.SR_Circunflexo, false);
                if (tokenAtual.getTipo() == TipoToken.Identificador) {
                    obterSimbolo();
                } else {
                    TipoToken[] tipos = {TipoToken.PR_Literal, TipoToken.PR_Inteiro,
                        TipoToken.PR_Real, TipoToken.PR_Logico};
                    reconheceToken(tipos, true);
                }
                break;
            default:
                erro();
        }
    }

    /**
     * parametro : ('var')? identificador (',' identificador)* ':' ('^')?
     * (('literal' | 'inteiro' | 'real' | 'logico') | IDENTIFICADOR) (','
     * parametro)* ;
     */
    public void parametro() {
        reconheceToken(TipoToken.PR_Var, false);
        identificador();
        while (tokenAtual.getTipo() == TipoToken.SR_Virgula) {
            obterSimbolo();
            identificador();
        }
        reconheceToken(TipoToken.SR_DoisPontos, true);
        reconheceToken(TipoToken.SR_Circunflexo, false);
        if (tokenAtual.getTipo() == TipoToken.Identificador) {
            obterSimbolo();
        } else {
            TipoToken[] tipos = {TipoToken.PR_Literal, TipoToken.PR_Inteiro,
                TipoToken.PR_Real, TipoToken.PR_Logico};
            reconheceToken(tipos, true);
        }
        if (tokenAtual.getTipo() == TipoToken.SR_Virgula) {
            obterSimbolo();
            parametro();
        }
    }

    /**
     * cmd : 'leia' '(' identificador (',' identificador)* ')' | 'escreva' '('
     * expressao (',' expressao)* ')' | 'se' expressao 'entao' (cmd)* ('senao'
     * (cmd)*)? 'fimse' | 'caso' exp_aritmetica 'seja' (constantes ':' (cmd)*)+
     * ('senao' (cmd)*)? 'fimcaso' | 'para' IDENTIFICADOR '&lt;-' exp_aritmetica
     * 'ate' exp_aritmetica 'faca' (cmd)* 'fimpara' | 'enquanto' expressao
     * 'faca' (cmd)* 'fimenquanto' | 'faca' (cmd)* 'ate' expressao | '^'
     * IDENTIFICADOR ('.' IDENTIFICADOR)* dimensao '&lt;-' expressao |
     * IDENTIFICADOR chamada_atribuicao | 'retorne' expressao* ;
     */
    public void cmd() {
        switch (tokenAtual.getTipo()) {
            case PR_Leia:
                obterSimbolo();
                reconheceToken(TipoToken.SR_AbreParenteses, true);
                identificador();
                // (',' identificador)*
                while (tokenAtual.getTipo() == TipoToken.SR_Virgula) {
                    obterSimbolo();
                    identificador();
                }
                reconheceToken(TipoToken.SR_FechaParenteses, true);
                break;
            case PR_Escreva:
                obterSimbolo();
                reconheceToken(TipoToken.SR_AbreParenteses, true);
                expressao();
                while (tokenAtual.getTipo() == TipoToken.SR_Virgula) {
                    obterSimbolo();
                    expressao();
                }
                reconheceToken(TipoToken.SR_FechaParenteses, true);
                break;
            case PR_Se:
                obterSimbolo();
                expressao();
                reconheceToken(TipoToken.PR_Entao, true);
                while (tokenAtual.getTipo() == TipoToken.PR_Leia
                        || tokenAtual.getTipo() == TipoToken.PR_Escreva
                        || tokenAtual.getTipo() == TipoToken.PR_Se
                        || tokenAtual.getTipo() == TipoToken.PR_Caso
                        || tokenAtual.getTipo() == TipoToken.PR_Para
                        || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                        || tokenAtual.getTipo() == TipoToken.PR_Faca
                        || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                        || tokenAtual.getTipo() == TipoToken.Identificador
                        || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                    cmd();
                }
                if (tokenAtual.getTipo() == TipoToken.PR_Senao) {
                    obterSimbolo();
                    while (tokenAtual.getTipo() == TipoToken.PR_Leia
                            || tokenAtual.getTipo() == TipoToken.PR_Escreva
                            || tokenAtual.getTipo() == TipoToken.PR_Se
                            || tokenAtual.getTipo() == TipoToken.PR_Caso
                            || tokenAtual.getTipo() == TipoToken.PR_Para
                            || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                            || tokenAtual.getTipo() == TipoToken.PR_Faca
                            || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                            || tokenAtual.getTipo() == TipoToken.Identificador
                            || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                        cmd();
                    }
                }
                reconheceToken(TipoToken.PR_FimSe, true);
                break;
            case PR_Caso:
                obterSimbolo();
                expAritmetica();
                reconheceToken(TipoToken.PR_Seja, true);
                constantes();
                reconheceToken(TipoToken.SR_DoisPontos, true);
                while (tokenAtual.getTipo() == TipoToken.PR_Leia
                        || tokenAtual.getTipo() == TipoToken.PR_Escreva
                        || tokenAtual.getTipo() == TipoToken.PR_Se
                        || tokenAtual.getTipo() == TipoToken.PR_Caso
                        || tokenAtual.getTipo() == TipoToken.PR_Para
                        || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                        || tokenAtual.getTipo() == TipoToken.PR_Faca
                        || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                        || tokenAtual.getTipo() == TipoToken.Identificador
                        || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                    cmd();
                }
                while (tokenAtual.getTipo() == TipoToken.SR_Subtracao
                        || tokenAtual.getTipo() == TipoToken.NumeroInteiro) {
                    constantes();
                    reconheceToken(TipoToken.SR_DoisPontos, true);
                    while (tokenAtual.getTipo() == TipoToken.PR_Leia
                            || tokenAtual.getTipo() == TipoToken.PR_Escreva
                            || tokenAtual.getTipo() == TipoToken.PR_Se
                            || tokenAtual.getTipo() == TipoToken.PR_Caso
                            || tokenAtual.getTipo() == TipoToken.PR_Para
                            || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                            || tokenAtual.getTipo() == TipoToken.PR_Faca
                            || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                            || tokenAtual.getTipo() == TipoToken.Identificador
                            || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                        cmd();
                    }
                }
                if (tokenAtual.getTipo() == TipoToken.PR_Senao) {
                    obterSimbolo();
                    while (tokenAtual.getTipo() == TipoToken.PR_Leia
                            || tokenAtual.getTipo() == TipoToken.PR_Escreva
                            || tokenAtual.getTipo() == TipoToken.PR_Se
                            || tokenAtual.getTipo() == TipoToken.PR_Caso
                            || tokenAtual.getTipo() == TipoToken.PR_Para
                            || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                            || tokenAtual.getTipo() == TipoToken.PR_Faca
                            || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                            || tokenAtual.getTipo() == TipoToken.Identificador
                            || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                        cmd();
                    }
                }
                reconheceToken(TipoToken.PR_FimCaso, true);
                break;
            case PR_Para:
                obterSimbolo();
                reconheceToken(TipoToken.Identificador, true);
                reconheceToken(TipoToken.SR_Atribuicao, true);
                expAritmetica();
                reconheceToken(TipoToken.PR_Ate, true);
                expAritmetica();
                reconheceToken(TipoToken.PR_Faca, true);
                while (tokenAtual.getTipo() == TipoToken.PR_Leia
                        || tokenAtual.getTipo() == TipoToken.PR_Escreva
                        || tokenAtual.getTipo() == TipoToken.PR_Se
                        || tokenAtual.getTipo() == TipoToken.PR_Caso
                        || tokenAtual.getTipo() == TipoToken.PR_Para
                        || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                        || tokenAtual.getTipo() == TipoToken.PR_Faca
                        || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                        || tokenAtual.getTipo() == TipoToken.Identificador
                        || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                    cmd();
                }
                reconheceToken(TipoToken.PR_FimPara, true);
                break;
            case PR_Enquanto:
                obterSimbolo();
                expressao();
                reconheceToken(TipoToken.PR_Faca, true);
                while (tokenAtual.getTipo() == TipoToken.PR_Leia
                        || tokenAtual.getTipo() == TipoToken.PR_Escreva
                        || tokenAtual.getTipo() == TipoToken.PR_Se
                        || tokenAtual.getTipo() == TipoToken.PR_Caso
                        || tokenAtual.getTipo() == TipoToken.PR_Para
                        || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                        || tokenAtual.getTipo() == TipoToken.PR_Faca
                        || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                        || tokenAtual.getTipo() == TipoToken.Identificador
                        || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                    cmd();
                }
                reconheceToken(TipoToken.PR_FimEnquanto, true);
                break;
            case PR_Faca:
                obterSimbolo();
                while (tokenAtual.getTipo() == TipoToken.PR_Leia
                        || tokenAtual.getTipo() == TipoToken.PR_Escreva
                        || tokenAtual.getTipo() == TipoToken.PR_Se
                        || tokenAtual.getTipo() == TipoToken.PR_Caso
                        || tokenAtual.getTipo() == TipoToken.PR_Para
                        || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                        || tokenAtual.getTipo() == TipoToken.PR_Faca
                        || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                        || tokenAtual.getTipo() == TipoToken.Identificador
                        || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                    cmd();
                }
                reconheceToken(TipoToken.PR_Ate, true);
                expressao();
                break;
            case SR_Circunflexo:
                obterSimbolo();
                reconheceToken(TipoToken.Identificador, true);
                while (tokenAtual.getTipo() == TipoToken.SR_Ponto) {
                    obterSimbolo();
                    reconheceToken(TipoToken.Identificador, true);
                }
                dimensao();
                reconheceToken(TipoToken.SR_Atribuicao, true);
                expressao();
                break;
            case Identificador:
                obterSimbolo();
                chamadaAtribuicao();
                break;
            case PR_Retorne:
                obterSimbolo();
                expressao();
                break;
            default:
                erro();
        }
    }

    /**
     * chamada_atribuicao : '(' expressao (',' expressao)* ')' | ('.'
     * IDENTIFICADOR)* dimensao '&lt;-' expressao ;
     */
    public void chamadaAtribuicao() {
        switch (tokenAtual.getTipo()) {
            case SR_AbreParenteses:
                obterSimbolo();
                expressao();
                while (tokenAtual.getTipo() == TipoToken.SR_Virgula) {
                    obterSimbolo();
                    expressao();
                }
                reconheceToken(TipoToken.SR_FechaParenteses, true);
                break;
            case SR_Ponto:
            case SR_AbreColchetes:
            case SR_Atribuicao:
                while (tokenAtual.getTipo() == TipoToken.SR_Ponto) {
                    obterSimbolo();
                    reconheceToken(TipoToken.Identificador, true);
                }
                dimensao();
                reconheceToken(TipoToken.SR_Atribuicao, true);
                expressao();
                break;
            default:
                erro();
        }
    }

    /**
     * constantes : ('-')? NUMERO_INTEIRO ('..' ('-')? NUMERO_INTEIRO)* (','
     * constantes)? ;
     */
    public void constantes() {
        reconheceToken(TipoToken.SR_Subtracao, false);
        reconheceToken(TipoToken.NumeroInteiro, true);
        while (tokenAtual.getTipo() == TipoToken.SR_Concatenacao) {
            obterSimbolo();
            reconheceToken(TipoToken.SR_Subtracao, false);
            reconheceToken(TipoToken.NumeroInteiro, true);
        }
        if (tokenAtual.getTipo() == TipoToken.SR_Virgula) {
            obterSimbolo();
            constantes();
        }
    }

    /**
     * exp_aritmetica : termo (('+'|'-') termo)* ;
     */
    public void expAritmetica() {
        termo();
        while (tokenAtual.getTipo() == TipoToken.SR_Adicao
                || tokenAtual.getTipo() == TipoToken.SR_Subtracao) {
            TipoToken[] tipos = {TipoToken.SR_Adicao, TipoToken.SR_Subtracao};
            reconheceToken(tipos, true);
            termo();
        }
    }

    /**
     * termo : fator (('*'|'/') fator)* ;
     */
    public void termo() {
        fator();
        while (tokenAtual.getTipo() == TipoToken.SR_Multiplicacao
                || tokenAtual.getTipo() == TipoToken.SR_Divisao) {
            TipoToken[] tipos = {TipoToken.SR_Multiplicacao, TipoToken.SR_Divisao};
            reconheceToken(tipos, true);
            fator();
        }
    }

    /**
     * fator : parcela ('%' parcela)* ;
     */
    public void fator() {
        parcela();
        while (tokenAtual.getTipo() == TipoToken.SR_Modulo) {
            reconheceToken(TipoToken.SR_Modulo, true);
            parcela();
        }
    }

    /**
     * parcela : ('-')? parcela_unario | parcela_nao_unario ;
     */
    public void parcela() {
        switch (tokenAtual.getTipo()) {
            case SR_Subtracao:
            case SR_Circunflexo:
            case Identificador:
            case NumeroInteiro:
            case NumeroReal:
            case SR_AbreParenteses:
                reconheceToken(TipoToken.SR_Subtracao, false);
                parcelaUnario();
                break;
            case SR_Endereco:
            case CadeiaLiteral:
                parcelaNaoUnario();
                break;
            default:
                erro();
        }
    }

    /**
     * parcela_unario : '^' IDENTIFICADOR ('.' IDENTIFICADOR)* dimensao |
     * IDENTIFICADOR chamada_partes | NUMERO_INTEIRO | NUMERO_REAL | '('
     * expressao ')' ;
     */
    public void parcelaUnario() {
        switch (tokenAtual.getTipo()) {
            case SR_Circunflexo:
                obterSimbolo();
                reconheceToken(TipoToken.Identificador, true);
                //('.' IDENTIFICADOR)*
                while (tokenAtual.getTipo() == TipoToken.SR_Ponto) {
                    reconheceToken(TipoToken.SR_Ponto, true);
                    reconheceToken(TipoToken.Identificador, true);
                }
                dimensao();
                break;
            case Identificador:
                obterSimbolo();
                chamadaPartes();
                break;
            case NumeroInteiro:
                obterSimbolo();
                break;
            case NumeroReal:
                obterSimbolo();
                break;
            case SR_AbreParenteses:
                obterSimbolo();
                expressao();
                reconheceToken(TipoToken.SR_FechaParenteses, true);
                break;
            default:
                erro();
        }
    }

    /**
     * parcela_nao_unario : '&' IDENTIFICADOR ('.' IDENTIFICADOR)* dimensao |
     * CADEIA_LITERAL ;
     */
    public void parcelaNaoUnario() {
        switch (tokenAtual.getTipo()) {
            case SR_Endereco:
                obterSimbolo();
                reconheceToken(TipoToken.Identificador, true);
                //('.' IDENTIFICADOR)*
                while (tokenAtual.getTipo() == TipoToken.SR_Ponto) {
                    obterSimbolo();
                    reconheceToken(TipoToken.Identificador, true);
                }
                dimensao();
                break;
            case CadeiaLiteral:
                obterSimbolo();
                break;
            default:
                erro();
        }
    }

    /**
     * chamada_partes : '(' expressao (',' expressao)* ')' | ('.'
     * IDENTIFICADOR)* dimensao | ;
     */
    public void chamadaPartes() {

        switch (tokenAtual.getTipo()) {
            case SR_AbreParenteses:
                obterSimbolo();
                expressao();
                // (',' expressao)*
                while (tokenAtual.getTipo() == TipoToken.SR_Virgula) {
                    obterSimbolo();
                    expressao();
                }
                reconheceToken(TipoToken.SR_FechaParenteses, true);
                break;
            case SR_Ponto:
            case SR_AbreColchetes:
                // ('.' IDENTIFICADOR)*
                while (tokenAtual.getTipo() == TipoToken.SR_Ponto) {
                    obterSimbolo();
                    reconheceToken(TipoToken.Identificador, true);
                }
                dimensao();
                break;
        }
    }

    /**
     * exp_relacional : exp_aritmetica (('='|'&lt;>'|'>='|'&lt;='|'>'|'&lt;')
     * exp_aritmetica)* ;
     */
    public void expRelacional() {
        expAritmetica();
        TipoToken[] tipos = {TipoToken.SR_Igual, TipoToken.SR_Diferente,
            TipoToken.SR_MaiorIgual, TipoToken.SR_MenorIgual,
            TipoToken.SR_Maior, TipoToken.SR_Menor};

        // (('='|'&lt;>'|'>='|'&lt;='|'>'|'&lt;') exp_aritmetica)*
        while (tokenAtual.getTipo() == TipoToken.SR_Igual
                || tokenAtual.getTipo() == TipoToken.SR_Diferente
                || tokenAtual.getTipo() == TipoToken.SR_MaiorIgual
                || tokenAtual.getTipo() == TipoToken.SR_MenorIgual
                || tokenAtual.getTipo() == TipoToken.SR_Maior
                || tokenAtual.getTipo() == TipoToken.SR_Menor) {

            reconheceToken(tipos, true);
            expAritmetica();
        }
    }

    /**
     * expressao : termo_logico ('ou' termo_logico)* ;
     */
    public void expressao() {
        termoLogico();
        // ('ou' termo_logico)
        while (tokenAtual.getTipo() == TipoToken.PR_Ou) {
            obterSimbolo();
            termoLogico();
        }
    }

    /**
     * termo_logico : ('nao')? parcela_logica ('e' ('nao')? parcela_logica)* ;
     */
    public void termoLogico() {
        reconheceToken(TipoToken.PR_Nao, false);
        parcelaLogica();
        // ('e' ('nao')? parcela_logica)*
        while (tokenAtual.getTipo() == TipoToken.PR_E) {
            obterSimbolo();
            reconheceToken(TipoToken.PR_Nao, false);
            parcelaLogica();
        }
    }

    /**
     * parcela_logica : 'verdadeiro' | 'falso' | exp_relacional ;
     */
    public void parcelaLogica() {
        switch (tokenAtual.getTipo()) {
            case PR_Verdadeiro:
                reconheceToken(TipoToken.PR_Verdadeiro, true);
                break;
            case PR_Falso:
                reconheceToken(TipoToken.PR_Falso, true);
                break;
            default:
                expRelacional();
        }
    }
}
